<?php
/**
 * Ganglib
 *
 * LICENSE
 *
 * This source file is subject to the GNU Lesser Generak Public license.
 * 
 * It is available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/lgpl.html
 *
 * @category    Ganglib
 * @package    Div_Cache
 * @copyright  Copyright (c) 2008 Ganglib project members (http://www.code.google.com/p/ganglib)
 * @license     http://www.gnu.org/licenses/lgpl.html
 */
 
/**
 * @see Zend_Cache_Core
 */
require_once 'Zend/Cache/Core.php';

/**
 * Cache Manager. 
 * 
 * Cache manager simplifies configuration of Zend_Cache objects. Also using
 * Cache Manager You could reduce dependencies of Zend_Cache specific drivers.
 *
 * @package    Div_Cache
 * @copyright  Copyright (c) 2008 Ganglib project members (http://www.code.google.com/p/ganglib)
 * @license     http://www.gnu.org/licenses/lgpl.html
 */
class Div_Cache_Manager
{
	/**
	 * Config 
	 *
	 * @var Zend_Config
	 */
	protected $_config;
	
	/**
	 * Avaible cache pool
	 *
	 * @var array
	 */
	protected $_cachePool = array();
	
	/**
	 * Self Instance
	 *
	 * @var Div_Db_Distributor
	 */
	static private $instance;
	
	/**
	 * Constructor
	 *
	 * @param Zend_Config $config
	 * @access public
	 * @throws Zend_Cache_Exception
	 * @return void
	 */
	public function __construct(Zend_Config $config)
	{
		if(!isset($config->default))
			self::throwException('No default cache config');
		
		$this->_config = $config;
	}
	
	/**
	 * Gets singleton object instance
	 *
	 * @param Zend_Config $config for first use config has to be sent to cache manager
	 * @access public
	 * @return Div_Cache_Manager
	 */
	public static function getInstance(Zend_Config $config = null)
	{
		if(!self::$instance)
			self::$instance = new Div_Cache_Manager($config);
		
		return self::$instance;
	}
	
	/**
	 * Get Zend_Cache object
	 *
	 * @param string $id Unique cache name from $this->_config
	 * @param mixed $lifetime Cache lifetime
	 * @access public
	 * @throws Zend_Cache_Exception
	 * @return Zend_Cache
	 */
	public function get($id, $lifetime = null)
	{
		$cacheName = $id.$lifetime;

		// cache exists in pool so lets return it
		if(in_array($cacheName, $this->_cachePool))
			return $this->_cachePool[$cacheName];

		// set up new Zend_Cache object
		$cache = $this->_set($id, $lifetime);
		$this->_cachePool[$cacheName] = $cache;
		return $cache;
	}
	
	/**
	 * Sets new Zend_Cache object and returns it
	 * 
	 * Optionally, if $lifetime is set - use this (not configuration lifetime)
	 *
	 * @param string $id
	 * @param mixed $lifetime
	 * @access private
	 * @throws Zend_Cache_Exception
	 * @return Zend_Cache
	 */
	private function _set($id, $lifetime = null)
	{
		if(!isset($this->_config->{$id}))
			self::throwException('Cannot set new cache. Cache with id: '.$id.' does not exists in configuration');
			
		$frontend = $this->_config->{$id}->frontend->toArray();
		$backend = $this->_config->{$id}->backend->toArray();
		
		if($lifetime)
			$frontend['options']['lifetime'] = (int)$lifetime;

		require_once('Zend/Cache.php');
		return Zend_Cache::factory($frontend['name'], $backend['name'], $frontend['options'], $backend['options']);
	}
	
    /**
     * Throw an exception
     *
     * Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic
     * @param  string $msg  Message for the exception
     * @throws Zend_Cache_Exception
     */
    public static function throwException($msg)
    {
        // For perfs reasons, we use this dynamic inclusion
        require_once 'Div/Cache/Manager/Exception.php';
        throw new Div_Cache_Manager_Exception($msg);
    }
    
    /**
     * Remove all cache keys combined with specified $tag
     * 
     * @param mixed $cache Zend_Cache or string with cache name
     * @param string $tag
     */
    public function clearTag($cache, $tag)
    {
    	if(is_string($cache))
    		$cache = $this->get($cache);
    	
    	if($cache == null)
    		return false;
    		
    	if(! ($cache instanceof Zend_Cache_Core))
    		self :: throwException('Cache has to be valid Zend_cache object or valid cache-pool identifier');
    		
    	$tagKey = md5($tag.'cacheKeys');
    	
    	$keys = $cache->load($tagKey);
   	
    	if(!is_array($keys))
    		return false;
    	else
    	{
    		foreach($keys as $key => $val)
    		{
    			$cache->remove($key);
    		}
    	}
    	
    	return true;
    	
    }
    
    /**
     * Tag cache key. Tagged cache keys could be deleted at once
     * 
     * @param mixed $cache
     * @param string $tag name
     * @param string $cacheKey
     * @return array $keys
     */
    public function tagIt($cache, $tag, $cacheKey)
    {
    	if(is_string($cache))
    		$cache = $this->get($cache);


    	if($cache == null)
    		return false;
    		
    	if(! ($cache instanceof Zend_Cache_Core))
    		self :: throwException('Cache has to be valid Zend_cache object or valid cache-pool identifier');
    		
    	$tagKey = md5($tag.'cacheKeys');
    	$keys   = $cache->load($tagKey);
    	
    	if(!is_array($keys))
    		$keys = array();
    		
    	$keys[$cacheKey] = true;
    	
    	$cache->save($keys, $tagKey);
    	
    	return $keys;
    }
    
}